#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <pfs.h>
#include <endianhelper.h>

#include <string>

#include "common.h"

// global variables
static int              g_nwords = 10;

static Path             g_dict_name;

static int              g_word_length;

static file_handle_type g_dict_handle;

static const char      *g_word;

static int              g_nentries;

static const char      *g_appname;

static void
usage()
{
    fprintf(stderr, "%s [OPTIONS] word\n", g_appname);
    fprintf(stderr, "   -n number of words completed, default 10\n");
    fprintf(stderr, "   -d dictionary name, default dict.dat\n");
    fprintf(stderr, "   -h --help, show this help\n");

    exit(1);
}

static int
_handle_opts(int argc, char **argv)
{
    int i;
    for (i = 1; i < argc; i++) {
        std::string s(argv[i]);

        if (s == "-n") {
            if (i + 1 < argc)
                g_nwords = atoi(argv[++i]);

            if (g_nwords <= 0)
                g_nwords = 10;
        }
        else if (s == "-d") {
            if (i + 1 < argc) {
                g_dict_name.assign_mbs(argv[++i]);
            }
        }
        else if (s == "-h" || s == "--help") {
            usage();
        }
        else {
            break;
        }
    }

    return i;
}

static char *
_read_word_by_index(int i)
{
    if (i < 0)
        return NULL;

    pfs_int64 pos;

    pos = (pfs_int64) (12 + (i * g_word_length));

    if (pfs_seek(g_dict_handle, pos, PSM_BEGIN) != pos)
        return NULL;

    char *word = (char *) malloc (g_word_length);

    if (pfs_read(g_dict_handle, word, g_word_length) != g_word_length) {
        free(word);
        return NULL;
    }

    if (word[g_word_length - 1] != 0)
        word[g_word_length - 1] = 0;

    return word;
}

static int
_print_from_index(int i)
{
    int from, to;

    from = i;
    to = i + g_nwords;

    if (to > g_nentries) {
        int d = to - g_nentries;

        to   -= d;
        from -= d;
    }

    for (i = from; i < to; i++) {
        char *p = _read_word_by_index(i);
        if (p) {
            printf("%s\n", p);
            free(p);
        }
    }

    return 0;
}

static int
_do_complete(int left, int right)
{
    int middle;

    if (left >= right) {
        _print_from_index(left);
        return 0;
    }

    middle = (left + right) / 2;

    char *word = _read_word_by_index(middle);

    if (word) {
        int cmp = strcmp(g_word, word);

        if (cmp < 0)
            return _do_complete(left, middle);
        else if (cmp > 0)
            return _do_complete(middle + 1, right);
        else {
            _print_from_index(middle);
            return 0;
        }

        free(word);
    }

    return -1;
}

int main(int argc, char *argv[])
{
    g_appname = argv[0];

    if (argc < 2)
        usage();

    int i = _handle_opts(argc, argv);

    if (i == argc)
        usage();

    if (g_dict_name.name == NULL)
        g_dict_name.assign_mbs("dict.dat");

    std::string word(argv[i]);

    file_handle_type handle;

    handle = pfs_open(g_dict_name, POM_READ | POM_OPEN_EXISTS);

    if (!PFS_VALID_HANDLE_P(handle)) {
        fprintf(stderr, "open dict failed\n");
        return 1;
    }

    char ib[4];

    pfs_int64 r;

    if ((r = pfs_read(handle, ib, 4)) != 4) {
        fprintf(stderr, "can't read indent, %d\n", (int) r);
        return 1;
    }

    int ident = endian_mtoi_le(ib);
    if (ident != DICT_IDENT) {
        fprintf(stderr, "invalid indent 0x%x\n", (unsigned int) ident);
        return 1;
    }

    if (pfs_read(handle, ib, 4) != 4) {
        fprintf(stderr, "can't read version\n");
        return  1;
    }

    int version = endian_mtoi_le(ib);

    if (version != DICT_VERSION) {
        fprintf(stderr, "unsupport version %d, need %d\n", version, DICT_VERSION);
        return 1;
    }

    if (pfs_read(handle, ib, 4) != 4) {
        fprintf(stderr, "can't read word length\n");
        return 1;
    }

    int word_length;

    word_length = endian_mtoi_le(ib);

    if (word_length <= 0) {
        fprintf(stderr, "invalid word length %d\n", word_length);
        return 1;
    }

    g_word_length = word_length;

    g_dict_handle = handle;

    g_word = word.c_str();

    int size = pfs_get_file_size(handle);

    int nentries = (size - 12) / g_word_length;

    if (nentries <= 0) {
        fprintf(stderr, "the dict is empty\n");
        return 1;
    }

    g_nentries = nentries;

    return _do_complete(0, nentries);
}
